#include <jni.h>
#include <string>
#include <android/log.h>

//专门处理java层的bitmap对象，转换成c 语言识别的bitmap对象
//这个是要引入系统库在cmakelist里面
#include <android/bitmap.h>
//引入C的文件
extern "C" {
#include "gif_lib.h"
}

//定义颜色转换
//#define argb(a, r, g, b) (((a)& 0xff)<<24) | (((r) & 0xff)<<16) | (((g)&0xff)<<8) | ((b)&0xff)
#define  argb(a, r, g, b) ( ((a) & 0xff) << 24 ) | ( ((b) & 0xff) << 16 ) | ( ((g) & 0xff) << 8 ) | ((r) & 0xff)

//#define argb(a, r, g, b) (((a) & 0xff) << 24) | (((r) & 0xff) << 16) | (((g) & 0xff) << 8) | ((b) & 0xff)


#define  LOG_TAG    "GifTest"
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define  dispose(ext) (((ext)->Bytes[0] & 0x1c) >> 2)
#define  trans_index(ext) ((ext)->Bytes[3])
#define  transparency(ext) ((ext)->Bytes[0] & 1)

struct GifBean {
    //当前播放帧数
    int current_frame;
    //总帧数
    int total_frame;
    //每一帧的播放时间
    int *delays;
};


int drawBitmapFrame(GifFileType *gifFileType, AndroidBitmapInfo info, void *pixels) {


    GifBean *gifBean = static_cast<GifBean *>(gifFileType->UserData);
    SavedImage savedImage = gifFileType->SavedImages[gifBean->current_frame];


    //图像分两部分，一部分是图像信息，第二部分是图像描述信息
    GifImageDesc gifImageDesc = savedImage.ImageDesc;
    //获取颜色字典，后面颜色处理会用掉
//    ColorMapObject *colorMap = gifImageDesc.ColorMap;

    ColorMapObject *colorMap = gifImageDesc.ColorMap;
    if (colorMap == nullptr) {
        colorMap = gifFileType->SColorMap;
    }

    //bitmap信息
    int *px = static_cast<int *>(pixels);


    //每一行
    int *line;


    //这里是索引
    int pointPixel;

    //压缩的数据
    GifByteType gifByte;
    //真实的数据
    GifColorType colorType;


    //这里的top和Left是因为左边和上面是有距离的，不是图片的真实像素
    for (int y = gifImageDesc.Top; y < gifImageDesc.Top + gifImageDesc.Height; ++y) {
        //每次遍历行，把首地址给line
        line = px;
        for (int x = gifImageDesc.Left; x < gifImageDesc.Left + gifImageDesc.Width; ++x) {
            pointPixel = (y - gifImageDesc.Top) * gifImageDesc.Width + (x - gifImageDesc.Left);
//            这个取出来是一个压缩数据，需要字典来处理
            gifByte = savedImage.RasterBits[pointPixel];
            colorType = colorMap->Colors[gifByte];
            line[x] = argb(255, colorType.Red, colorType.Green, colorType.Blue);

        }

        //info.stride是byte， 要转成像素，一个像素4个字节
        //(char *) px - 这部分代码将px指针从指向int的指针转换为指向char的指针。因为char类型的指针是按字节递增的，这样的转换允许按字节对地址进行操作。本身这里的info.stride返回的就是字节
        px = (int *) ((char *) px + info.stride);
    }

    GraphicsControlBlock gcb;
    DGifSavedExtensionToGCB(gifFileType, gifBean->current_frame, &gcb);
    int delay = gcb.DelayTime * 10;

    return delay;
}

extern "C"
JNIEXPORT jlong JNICALL
Java_com_example_gifframecpp_GifHandler_loadGif(JNIEnv *env, jobject thiz, jstring path_) {
//    //转换成c语言识别的字符串
    const char *path = env->GetStringUTFChars(path_, nullptr);
    //打开成功还是失败
    int error;
    GifFileType *gifFileType = DGifOpenFileName(path, &error);


    //初始化缓冲区数组*savedImages
    DGifSlurp(gifFileType);

    //分配gifBean
    GifBean *gifBean = static_cast<GifBean *>(malloc(sizeof(GifBean)));
    //里面有脏数据，需要清除
    memset(gifBean, 0, sizeof(GifBean));

    //绑定数据，用于后续通过地址拿数据
    gifFileType->UserData = gifBean;
    //当前帧数
    gifBean->current_frame = 0;
    //总帧数
    gifBean->total_frame = gifFileType->ImageCount;

    //释放资源
    env->ReleaseStringUTFChars(path_, path);


    return reinterpret_cast<jlong>(gifFileType);

    //这样也可以
//    return (jlong) gifFileType;


    ;

}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_gifframecpp_GifHandler_getWidth(JNIEnv *env, jobject thiz, jlong gif_handler) {
    //地址还原成指针，直接强转
    GifFileType *gifFileType = reinterpret_cast<GifFileType *>(gif_handler);
    return gifFileType->SWidth;
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_gifframecpp_GifHandler_getHeight(JNIEnv *env, jobject thiz, jlong gif_handler) {
    GifFileType *gifFileType = reinterpret_cast<GifFileType *>(gif_handler);
    return gifFileType->SHeight;

}


// 绘制 bitmap
// GIF89a
int drawFrame(GifFileType *gif, AndroidBitmapInfo info, void *pixels, bool force_dispose_1) {
    GifColorType *bg;
    GifColorType *color;
    SavedImage *frame;
    ExtensionBlock *ext = 0;
    GifImageDesc *frameInfo;
    ColorMapObject *colorMap;
    int *line;
    int width, height, x, y, j, loc, n, inc, p;
    void *px;
    GifBean *gifBean = static_cast<GifBean *>(gif->UserData);
    width = gif->SWidth;
    height = gif->SHeight;
    frame = &(gif->SavedImages[gifBean->current_frame]);
    frameInfo = &(frame->ImageDesc);
    if (frameInfo->ColorMap) {
        colorMap = frameInfo->ColorMap;
    } else {
        colorMap = gif->SColorMap;
    }
    bg = &colorMap->Colors[gif->SBackGroundColor];
    for (j = 0; j < frame->ExtensionBlockCount; j++) {
        if (frame->ExtensionBlocks[j].Function == GRAPHICS_EXT_FUNC_CODE) {
            ext = &(frame->ExtensionBlocks[j]);
            break;
        }
    }
    // For dispose = 1, we assume its been drawn
    px = pixels;
    if (ext && dispose(ext) == 1 && force_dispose_1 && gifBean->current_frame > 0) {
        gifBean->current_frame = gifBean->current_frame - 1,
                drawFrame(gif, info, pixels, true);
    } else if (ext && dispose(ext) == 2 && bg) {
        for (y = 0; y < height; y++) {
            line = (int *) px;
            for (x = 0; x < width; x++) {
                line[x] = argb(255, bg->Red, bg->Green, bg->Blue);
            }
            px = (int *) ((char *) px + info.stride);
        }
    } else if (ext && dispose(ext) == 3 && gifBean->current_frame > 1) {
        gifBean->current_frame = gifBean->current_frame - 2,
                drawFrame(gif, info, pixels, true);
    }
    px = pixels;
    if (frameInfo->Interlace) {
        n = 0;
        inc = 8;
        p = 0;
        px = (int *) ((char *) px + info.stride * frameInfo->Top);
        for (y = frameInfo->Top; y < frameInfo->Top + frameInfo->Height; y++) {
            for (x = frameInfo->Left; x < frameInfo->Left + frameInfo->Width; x++) {
                loc = (y - frameInfo->Top) * frameInfo->Width + (x - frameInfo->Left);
                if (ext && frame->RasterBits[loc] == trans_index(ext) && transparency(ext)) {
                    continue;
                }
                color = (ext && frame->RasterBits[loc] == trans_index(ext)) ? bg
                                                                            : &colorMap->Colors[frame->RasterBits[loc]];
                if (color)
                    line[x] = argb(255, color->Red, color->Green, color->Blue);
            }
            px = (int *) ((char *) px + info.stride * inc);
            n += inc;
            if (n >= frameInfo->Height) {
                n = 0;
                switch (p) {
                    case 0:
                        px = (int *) ((char *) pixels + info.stride * (4 + frameInfo->Top));
                        inc = 8;
                        p++;
                        break;
                    case 1:
                        px = (int *) ((char *) pixels + info.stride * (2 + frameInfo->Top));
                        inc = 4;
                        p++;
                        break;
                    case 2:
                        px = (int *) ((char *) pixels + info.stride * (1 + frameInfo->Top));
                        inc = 2;
                        p++;
                }
            }
        }
    } else {
        px = (int *) ((char *) px + info.stride * frameInfo->Top);
        for (y = frameInfo->Top; y < frameInfo->Top + frameInfo->Height; y++) {
            line = (int *) px;
            for (x = frameInfo->Left; x < frameInfo->Left + frameInfo->Width; x++) {
                loc = (y - frameInfo->Top) * frameInfo->Width + (x - frameInfo->Left);
                if (ext && frame->RasterBits[loc] == trans_index(ext) && transparency(ext)) {
                    continue;
                }
                color = (ext && frame->RasterBits[loc] == trans_index(ext)) ? bg
                                                                            : &colorMap->Colors[frame->RasterBits[loc]];
                if (color)
                    line[x] = argb(255, color->Red, color->Green, color->Blue);
            }
            px = (int *) ((char *) px + info.stride);
        }
    }
    GraphicsControlBlock gcb;//获取控制信息
    DGifSavedExtensionToGCB(gif, gifBean->current_frame, &gcb);
    int delay = gcb.DelayTime * 10;
    LOGE("delay %d", delay);
    return delay;
}

extern "C"
JNIEXPORT jint JNICALL
Java_com_example_gifframecpp_GifHandler_updateFrame(JNIEnv *env, jobject thiz, jlong gif_handler,
                                                    jobject bitmap) {

    //一种方式来获取宽高
    GifFileType *gifFileType = reinterpret_cast<GifFileType *>(gif_handler);
//    int width = gifFileType->SWidth;
//    int height = gifFileType->SHeight;

    //第二种方式来获取宽高
    AndroidBitmapInfo bitmapInfo;
    AndroidBitmap_getInfo(env, bitmap, &bitmapInfo);
    int width = bitmapInfo.width;
    int height = bitmapInfo.height;

    void *pixel;

    //将bitmap转换成二维数组，另外就是锁住当前bitmap
    AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void **>(&pixel));

    int delay = drawFrame(gifFileType, bitmapInfo, pixel, false);

    AndroidBitmap_unlockPixels(env, bitmap);

    GifBean *gifBean = static_cast<GifBean *>(gifFileType->UserData);

    gifBean->current_frame++;
    if (gifBean->current_frame > gifBean->total_frame - 1) {
        gifBean->current_frame = 0;
    }
    return delay;


}
